//
// Created by ms on 2025/10/21.
//

#include "Commander.h"

#include <string.h>
#include <stdarg.h>
#include <stdio.h>

Commander::Commander(const char *soh, const char *midl, const char *eol) :
        _soh(soh), _midl(midl), _eol(eol) {

    soh_len  = strlen(_soh);
    midl_len = strlen(_midl);
    eol_len  = strlen(_eol);
}

int Commander::addCommand(Command *command) {

    if (searchCommand(command->id) != nullptr) {
        return -1;
    }

    command->ret_data = data_buffer;
    command_list.push_back(command);
    return 0;
}

Command *Commander::searchCommand(const char *id) {
    for (auto iter: command_list) {
        if (strcmp(id, iter->id) == 0) {
            return iter;
        }
    }
    return nullptr;
}

Command* Commander::parse(char *frame) {
    return parse((uint8_t *) frame, strlen(frame));
}

Command* Commander::parse(uint8_t *data, uint16_t length) {
    char *frame = (char *) data;

    if (frame == nullptr || length < soh_len + eol_len) {
        return nullptr;
    }

    /**
     * get head
     */
    int wild_len = 0;

    for (int i = 0; i < soh_len; i++) {
        if (_soh[i] == '*') {
            wildcards[wild_len++] = (char) frame[i];
            continue;
        }

        if (frame[i] != _soh[i]) {
            return nullptr;
        }
        wildcards[wild_len++] = _soh[i];
    }

    wildcards[wild_len] = '\0';

    /**
     * get eol
     */
    char *eol_ptr = strstr(frame + soh_len, _eol);
    if (!eol_ptr) {
        return nullptr;
    }

    /**
     * get midl
     */
    char *midl_prt = strstr(frame + soh_len, _midl);
    if (!midl_prt) {
        char chr_eol = *eol_ptr;
        *eol_ptr = '\0';

        /* no args command */
        auto no_args_cmd = searchCommand(frame + soh_len);
        if (no_args_cmd != nullptr) {
            no_args_cmd->data = nullptr;
            *data_buffer = '\0';
            if (no_args_cmd->callback) {
                no_args_cmd->callback(no_args_cmd);
            }
            pushCommandResultFrame(no_args_cmd);
            return no_args_cmd;
        }

        *eol_ptr = chr_eol;
        return nullptr;
    }

    /**
     * get command length
     */
    uint32_t id_len = (uint32_t) (midl_prt - frame) - soh_len;
    if (id_len == 0) {
        return nullptr;
    }

    /**
     * get args length
     */
    uint32_t arg_len = (uint32_t) (eol_ptr - midl_prt) - midl_len;

    char *id  = frame + soh_len;
    char *arg = midl_prt + midl_len;
    *(id + id_len)   = '\0';
    *(arg + arg_len) = '\0';

    /**
     * find command form list
     */
    Command *cmd = searchCommand(id);
    if (cmd == nullptr) {
        return nullptr;
    }

    cmd->data = (arg_len == 0 ? nullptr : arg);
    *data_buffer = '\0';
    if (cmd->callback) {
        cmd->callback(cmd);
    }
    pushCommandResultFrame(cmd);
    return cmd;
}

void Commander::pushCommandResultFrame(Command *cmd) {

    char *data = (strlen(cmd->ret_data) == 0 ? (char *)cmd->def_ret : cmd->ret_data);

    sprintf(result_buffer, "%s%s%s%s%s",
            wildcards, cmd->id, _midl, data, _eol);
}

size_t Commander::getResult(uint8_t *data, uint16_t length) {
    length -= 1;

    auto data_len = strlen(result_buffer);
    data_len = (data_len > length ? length : data_len);
    strncpy((char *) data, result_buffer, data_len);
    *(data + data_len) = '\0';
    return data_len;
}


int Command::pushResult(const char *fmt, ...) const {
    if (ret_data == nullptr) {
        return 0;
    }

    va_list arg_ptr;

    va_start(arg_ptr, fmt);
    int data_len = vsprintf(ret_data, fmt, arg_ptr);
    va_end(arg_ptr);


    return data_len;
}
